--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit ca00eaeba74a10bfb268c4487a755f2f4759ab68
Parents : 88272d1
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-05-18T05:08:56-05:00
fix(ui): improve relative timestamp granularity with combined units
Changes
2 files changed, 32 insertions(+), 21 deletions(-)
Diff
diff --git a/meshchatx/src/frontend/js/Utils.js b/meshchatx/src/frontend/js/Utils.js
index ef3d218e..34b9d210 100644
--- a/meshchatx/src/frontend/js/Utils.js
+++ b/meshchatx/src/frontend/js/Utils.js
@@ -48,32 +48,37 @@ class Utils {
static formatSecondsWithoutAgo(seconds) {
const parsedSeconds = this.parseSeconds(seconds);
+ const parts = [];
if (parsedSeconds.days > 0) {
- if (parsedSeconds.days === 1) {
- return "1 day";
- }
- return parsedSeconds.days + " days";
+ parts.push(parsedSeconds.days === 1 ? "1 day" : parsedSeconds.days + " days");
}
if (parsedSeconds.hours > 0) {
- if (parsedSeconds.hours === 1) {
- return "1 hour";
- }
- return parsedSeconds.hours + " hours";
+ parts.push(parsedSeconds.hours === 1 ? "1 hour" : parsedSeconds.hours + " hours");
}
if (parsedSeconds.minutes > 0) {
- if (parsedSeconds.minutes === 1) {
- return "1 min";
+ parts.push(parsedSeconds.minutes === 1 ? "1 minute" : parsedSeconds.minutes + " minutes");
+ }
+
+ if (parts.length === 0) {
+ if (parsedSeconds.seconds <= 1) {
+ return "a second";
}
- return parsedSeconds.minutes + " mins";
+ return parsedSeconds.seconds + " seconds";
+ }
+
+ if (parts.length === 1) {
+ return parts[0];
}
- if (parsedSeconds.seconds <= 1) {
- return "a second";
+ // Join with commas and "and" for the last part
+ const last = parts.pop();
+ if (parts.length === 1) {
+ return parts[0] + " and " + last;
}
- return parsedSeconds.seconds + " seconds";
+ return parts.join(", ") + ", and " + last;
}
static formatSeconds(seconds) {
diff --git a/tests/frontend/Utils.test.js b/tests/frontend/Utils.test.js
index 7e2e0193..cf8c9679 100644
--- a/tests/frontend/Utils.test.js
+++ b/tests/frontend/Utils.test.js
@@ -73,8 +73,8 @@ describe("Utils.js", () => {
});
it("formats minutes ago", () => {
- expect(Utils.formatSeconds(60)).toBe("1 min ago");
- expect(Utils.formatSeconds(120)).toBe("2 mins ago");
+ expect(Utils.formatSeconds(60)).toBe("1 minute ago");
+ expect(Utils.formatSeconds(120)).toBe("2 minutes ago");
});
it("formats hours ago", () => {
@@ -86,6 +86,12 @@ describe("Utils.js", () => {
expect(Utils.formatSeconds(86400)).toBe("1 day ago");
expect(Utils.formatSeconds(172800)).toBe("2 days ago");
});
+
+ it("formats combined units granularly", () => {
+ expect(Utils.formatSeconds(90061)).toBe("1 day, 1 hour, and 1 minute ago");
+ expect(Utils.formatSeconds(172860)).toBe("2 days and 1 minute ago");
+ expect(Utils.formatSeconds(3661)).toBe("1 hour and 1 minute ago");
+ });
});
describe("formatSecondsWithoutAgo", () => {
@@ -116,8 +122,8 @@ describe("Utils.js", () => {
const now = new Date("2025-01-01T12:00:00Z");
vi.setSystemTime(now);
const pastDate = "2025-01-01 11:59:00";
- expect(Utils.formatTimeAgoForI18n(pastDate)).toBe("1 min");
- expect(Utils.formatTimeAgo(pastDate)).toBe("1 min ago");
+ expect(Utils.formatTimeAgoForI18n(pastDate)).toBe("1 minute");
+ expect(Utils.formatTimeAgo(pastDate)).toBe("1 minute ago");
});
});
@@ -134,7 +140,7 @@ describe("Utils.js", () => {
const nowSec = Math.floor(new Date("2025-06-01T15:00:00Z").getTime() / 1000);
vi.setSystemTime(new Date("2025-06-01T15:00:00Z"));
expect(Utils.formatSecondsAgoForI18n(nowSec - 10)).toBe("less than a minute");
- expect(Utils.formatSecondsAgoForI18n(nowSec - 120)).toBe("2 mins");
+ expect(Utils.formatSecondsAgoForI18n(nowSec - 120)).toBe("2 minutes");
});
});
@@ -151,8 +157,8 @@ describe("Utils.js", () => {
const now = new Date("2025-01-01T12:00:00Z");
vi.setSystemTime(now);
- const pastDate = "2025-01-01 11:59:00"; // 1 min ago
- expect(Utils.formatTimeAgo(pastDate)).toBe("1 min ago");
+ const pastDate = "2025-01-01 11:59:00"; // 1 minute ago
+ expect(Utils.formatTimeAgo(pastDate)).toBe("1 minute ago");
});
it('returns "unknown" for empty input', () => {
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────